﻿using System.Configuration;
using System.Reflection;
using Castle.Core;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using Managistics.Model.Mappings;
using Managistics.Core.Dependencies.Uow;
using Managistics.Core.Repositories.Impl;
using Managistics.Core.Services.Impl;
using FluentNHibernate.Automapping;

namespace Managistics.Core.Dependencies
{
    public class WarehouseDependencyInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Kernel.ComponentRegistered += Kernel_ComponentRegistered;

            //Register all components
            container.Register(

                //Nhibernate session factory
                Component.For<ISessionFactory>().UsingFactoryMethod(CreateNhSessionFactory).LifeStyle.Singleton,

                //Unitofwork interceptor
                Component.For<NhUnitOfWorkInterceptor>().LifeStyle.Transient,

                //All repoistories
                Classes.FromAssembly(Assembly.GetAssembly(typeof(GoodRepository))).InSameNamespaceAs<GoodRepository>().WithService.DefaultInterfaces().LifestyleTransient(),

                Classes.FromAssembly(Assembly.GetAssembly(typeof(SaleOrderRepository))).InSameNamespaceAs<SaleOrderRepository>().WithService.DefaultInterfaces().LifestyleTransient(),

                //All services
                Classes.FromAssembly(Assembly.GetAssembly(typeof(GoodService))).InSameNamespaceAs<GoodService>().WithService.DefaultInterfaces().LifestyleTransient(),

                Classes.FromAssembly(Assembly.GetAssembly(typeof(SaleOrderService))).InSameNamespaceAs<SaleOrderService>().WithService.DefaultInterfaces().LifestyleTransient()

                );
        }

        /// <summary>
        /// Creates NHibernate Session Factory.
        /// </summary>
        /// <returns>NHibernate Session Factory</returns>
        private static ISessionFactory CreateNhSessionFactory()
        {
            var env = ConfigurationManager.AppSettings["Environment"].ToString();
            var connStr = ConfigurationManager.ConnectionStrings[env + "Conn"].ConnectionString;
            return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connStr).ShowSql())
                .Mappings(m => {
                    m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(GoodMap)));
                    m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(CustomerMap)));
                    m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(SupplierMap)));
                    m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(EmployeeMap)));
                    m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(SaleOrderMap)));
                })
                .ExposeConfiguration(config =>
                {
                    config.SetProperty("adonet.batch_size", "100");
                })
                .Diagnostics(d => d.Enable().OutputToConsole())
                .BuildSessionFactory();
        }

        void Kernel_ComponentRegistered(string key, Castle.MicroKernel.IHandler handler)
        {
            //Intercept all methods of all repositories.
            if (UnitOfWorkHelper.IsRepositoryClass(handler.ComponentModel.Implementation))
            {
                handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(NhUnitOfWorkInterceptor)));
            }

            //Intercept all methods of classes those have at least one method that has UnitOfWork attribute.
            foreach (var method in handler.ComponentModel.Implementation.GetMethods())
            {
                if (UnitOfWorkHelper.HasUnitOfWorkAttribute(method))
                {
                    handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(NhUnitOfWorkInterceptor)));
                    return;
                }
            }
        }
    }
}